package org.example.search.service.impl;

import cn.hutool.core.util.StrUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.*;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import lombok.extern.slf4j.Slf4j;
import org.example.core.constant.TokenConstants;
import org.example.core.exception.CustomException;
import org.example.core.exception.ExceptionCode;
import org.example.core.utils.R;
import org.example.search.api.MemberService;
import org.example.search.constant.ElasticConstants;
import org.example.search.module.SkuEsModule;
import org.example.search.service.EsProductFrontService;
import org.example.search.vo.*;
import org.example.security.handler.SecurityHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 小杰_cc
 * @version 1.0
 */
@Service
@Slf4j
public class EsProductFrontServiceImpl implements EsProductFrontService {

    @Autowired
    private ElasticsearchClient elasticsearchClient;
    @Autowired
    private MemberService memberService;

    // 商品检索
    @Override
    public SearchRespVo search(SearchParamVo searchParamVo) {
        SearchResponse<SkuEsModule> searchResponse = null;
        try {
            searchResponse = elasticsearchClient.search(s -> {
                s.index(ElasticConstants.PRODUCT_INDEX);
                // 查询条件
                s.query(q -> q.bool(b -> {
                    // 根据关键字查询
                    if (StrUtil.isNotBlank(searchParamVo.getKeyword())) {
                        b.must(MatchQuery.of(m -> m
                                        .field("spuTitle")
                                        .query(searchParamVo.getKeyword()))
                                ._toQuery());
                    }
                    // 根据分类id查询(三级分类)
                    if (searchParamVo.getCategoryId() != null) {
                        b.filter(TermQuery.of(t -> t
                                        .field("categoryId")
                                        .value(searchParamVo.getCategoryId()))
                                ._toQuery());
                    }
                    // 根据品牌id查询
                    if (searchParamVo.getBrandId() != null) {
                        b.filter(TermQuery.of(t -> t
                                        .field("brandId")
                                        .value(searchParamVo.getBrandId()))
                                ._toQuery());
                    }
                    // 根据属性查询
                    if (searchParamVo.getAttributes() != null && !searchParamVo.getAttributes().isEmpty()) {
                        for (AttributeVo item : searchParamVo.getAttributes()){
                            if (item.getValue()!=null && !item.getValue().isEmpty()){
                                List<FieldValue> values = new ArrayList<>();
                                item.getValue().forEach(el->values.add(FieldValue.of(el)));
                                b.filter(NestedQuery.of(n -> n
                                        .path("attributes")
                                        .scoreMode(ChildScoreMode.None)
                                        .query(q2 -> q2
                                                .bool(b2 -> {
                                                    b2.must(TermQuery.of(t -> t
                                                                            .field("attributes.attributeId")
                                                                            .value(item.getAttributeId()))
                                                                    ._toQuery())
                                                            .must(TermsQuery.of(f->f
                                                                            .field("attributes.value")
                                                                            .terms(new TermsQueryField.Builder().value(values).build()))
                                                                    ._toQuery());
                                                    return b2;
                                                })
                                        )
                                )._toQuery());
                            }
                        }
                    }
                    // 是否有库存
                    b.filter(TermQuery.of(t -> t
                                    .field("hasStock")
                                    .value(true))
                            ._toQuery());
                    return b;
                }));
                // 排序 ASC表示升序排列,DESC表示降序排列
                if (searchParamVo.getPriceSort()!=null && searchParamVo.getPriceSort()==0){
                    s.sort(SortOptions.of(s2 -> s2.field(f -> f.field("price").order(searchParamVo.getPriceSort() == null ? SortOrder.Asc : SortOrder.Desc))));
                }
                // 分页
                s.from(searchParamVo.getPageNum() - 1).size(ElasticConstants.PAGE_SIZE);
                // 品牌聚合
                s.aggregations("brandAgg", agg1 -> agg1
                        .terms(t1 -> t1
                                .field("brandId")
                                .size(20))
                        .aggregations("brandNameAgg", agg2 -> agg2
                                .terms(t2 -> t2
                                        .field("brandName")
                                        .size(1)))
                        .aggregations("brandLogoUrlAgg", agg3 -> agg3
                                .terms(t3 -> t3
                                        .field("logoUrl")
                                        .size(1))));
                // 属性的聚合
                s.aggregations("attributesAgg", agg1 -> agg1
                        .nested(ne -> ne
                                .path("attributes"))
                        .aggregations("attributeIdAgg", agg2 -> agg2
                                .terms(t1 -> t1
                                        .field("attributes.attributeId")
                                        .size(10))
                                .aggregations("attributeNameAgg", agg3 -> agg3
                                        .terms(t2 -> t2
                                                .field("attributes.attributeName")
                                                .size(1)))
                                .aggregations("valueAgg", agg3 -> agg3
                                        .terms(t3 -> t3
                                                .field("attributes.value")
                                                .size(20)))));
                return s;
            }, SkuEsModule.class);
        }catch (IOException e){
            log.error("数据查询失败");
//            e.printStackTrace();
            throw new CustomException(ExceptionCode.SEARCH_SKU_ERROR.getCode(), ExceptionCode.SEARCH_SKU_ERROR.getMsg());
        }
        if (searchResponse == null){
            return new SearchRespVo();
        }
        return buildSearchResult(searchResponse, searchParamVo.getPageNum());
    }

    // 猜你喜欢
    @Override
    public DefaultRespVo like(Integer pageNum) {
        // TODO 需要加上分页条和搜索条件
        SearchResponse<SkuEsModule> searchResponse = null;
        try{
            searchResponse = elasticsearchClient.search(s -> {
                s.index(ElasticConstants.PRODUCT_INDEX);
                s.query(q -> q.term(TermQuery.of(f->f.field("hasStock").value(true))));
                // 分页
                s.from(pageNum - 1).size(ElasticConstants.PAGE_SIZE);
                // 排除掉attributes
                s.source(s1->s1.filter(f->f.excludes("attributes")));
                return s;
            }, SkuEsModule.class);
        }catch (IOException e){
            e.printStackTrace();
        }
        if (searchResponse == null){
            return null;
        }
        DefaultRespVo respVo = new DefaultRespVo();
        respVo.setSkus(searchResponse.hits().hits().stream().map(Hit::source).collect(Collectors.toList()));
        if(searchResponse.hits().total() == null){
            respVo.setTotal(0L);
            respVo.setPageNum(pageNum);
            respVo.setPages(0);
            return respVo;
        }
        long value = searchResponse.hits().total().value();
        respVo.setTotal(value);
        respVo.setPages((int)value%ElasticConstants.PAGE_SIZE ==0?(int)value/ElasticConstants.PAGE_SIZE:(int)value/ElasticConstants.PAGE_SIZE + 1);
        respVo.setPageNum(pageNum);
        return respVo;
    }

    /**
     * 根据查询收藏的商品信息
     */
    @Override
    public List<SkuEsModule> getEsCollectInfo() {
        SearchResponse<SkuEsModule> searchResponse = null;
        String memberId = SecurityHandler.getData().get(TokenConstants.MEMBER_ID);
        if (memberId == null){
            return null;
        }
        R<List<Long>> result = memberService.querySpuIds(memberId);

        List<Long> spuIds = result.getData();
        List<FieldValue> values = new ArrayList<>();
        if (spuIds != null && !spuIds.isEmpty()){
            for (Long spuId : spuIds) {
                values.add(FieldValue.of(spuId));
            }
        }else {
            return new ArrayList<>();
        }

        try{
            searchResponse = elasticsearchClient.search(s -> {
                s.index(ElasticConstants.PRODUCT_INDEX);
                s.query(q -> q
                        .terms(TermsQuery.of(f->f
                                .field("spuId")
                                .terms(new TermsQueryField.Builder().value(values).build())
                        ))
                );
                // 排除掉attributes
                s.source(s1->s1.filter(f->f.excludes("attributes")));
                return s;
            }, SkuEsModule.class);
        }catch (IOException e){
            e.printStackTrace();
        }
        if (searchResponse == null){
            return null;
        }
        List<SkuEsModule> collect = searchResponse.hits().hits().stream().map(Hit::source).collect(Collectors.toList());
        return collect;
    }

    /**
     * 同类推荐
     * @param categoryId
     * @return
     */
    @Override
    public List<SkuEsModule> getSameCategorySku(Long spuId, Long categoryId) {
        SearchResponse<SkuEsModule> searchResponse = null;
        try{
            searchResponse = elasticsearchClient.search(s -> {
                s.index(ElasticConstants.PRODUCT_INDEX);
                s.query(q -> q
                        .bool(b -> {
                            b.filter(TermQuery.of(t -> t
                                    .field("categoryId")
                                    .value(categoryId))._toQuery());
                            b.filter(TermQuery.of(t -> t
                                            .field("hasStock")
                                            .value(true))
                                    ._toQuery());
                            b.mustNot(TermQuery
                                    .of(t -> t
                                            .field("spuId")
                                            .value(spuId))
                                    ._toQuery());
                            return b;
                        }));
                s.from(0).size(ElasticConstants.PAGE_SIZE);
                // 排除掉attributes
                s.source(s1->s1.filter(f->f.excludes("attributes")));
                return s;
            }, SkuEsModule.class);
        }catch (IOException e){
            e.printStackTrace();
        }
        if (searchResponse == null){
            return null;
        }
        List<SkuEsModule> collect = searchResponse.hits().hits().stream().map(Hit::source).collect(Collectors.toList());
        return collect;
    }

    // 封装搜索结果聚合处理
    public SearchRespVo buildSearchResult(SearchResponse<SkuEsModule> searchResponse, Integer pageNum){
        SearchRespVo respVo = new SearchRespVo();
        List<SkuEsModule> skus = searchResponse.hits().hits().stream().map(Hit::source).collect(Collectors.toList());
        if(skus.isEmpty()){
            return respVo;
        }
        respVo.setSkus(skus);
        long value = searchResponse.hits().total().value();
        respVo.setTotal(value);
        respVo.setPages((int)value%ElasticConstants.PAGE_SIZE ==0?(int)value/ElasticConstants.PAGE_SIZE:(int)value/ElasticConstants.PAGE_SIZE + 1);
        respVo.setPageNum(pageNum);
        // 品牌聚合
        ArrayList<BrandVo> brandVos = new ArrayList<>();
        List<LongTermsBucket> brandBucket = searchResponse.aggregations().get("brandAgg").lterms().buckets().array();
        for (LongTermsBucket bucket : brandBucket) {
            long brandId = bucket.key();
            List<StringTermsBucket> brandNameAgg = bucket.aggregations().get("brandNameAgg").sterms().buckets().array();
            BrandVo brandVo = new BrandVo();
            if(!brandNameAgg.isEmpty()){
                String brandName = brandNameAgg.get(0).key().stringValue();
                brandVo.setBrandId(brandId);
                brandVo.setBrandName(brandName);
                brandVos.add(brandVo);
            }
            List<StringTermsBucket> brandNameLogo = bucket.aggregations().get("brandLogoUrlAgg").sterms().buckets().array();
            if(!brandNameLogo.isEmpty()){
                brandVo.setLogoUrl(brandNameLogo.get(0).key().stringValue());
            }
        };
        respVo.setBrands(brandVos);

        // 属性聚合
        ArrayList<AttributeVo> attributeVos = new ArrayList<>();
        NestedAggregate nestedAggregate = searchResponse.aggregations().get("attributesAgg").nested();
        List<LongTermsBucket> attributeIdBucket = nestedAggregate.aggregations().get("attributeIdAgg").lterms().buckets().array();
        for (LongTermsBucket bucket : attributeIdBucket) {
                long attributeId = bucket.key();
                List<StringTermsBucket> attributeNameAgg = bucket.aggregations().get("attributeNameAgg").sterms().buckets().array();
                AttributeVo attributeVo = new AttributeVo();
                if(!attributeNameAgg.isEmpty()){
                    String attributeName = attributeNameAgg.get(0).key().stringValue();
                    attributeVo.setAttributeId(attributeId);
                    attributeVo.setAttributeName(attributeName);
                }
                List<StringTermsBucket> valueBucket = bucket.aggregations().get("valueAgg").sterms().buckets().array();
                if (!valueBucket.isEmpty()){
                    List<String> collect = valueBucket.stream().map(item -> item.key().stringValue()).collect(Collectors.toList());
                    attributeVo.setValue(collect);
                }
                attributeVos.add(attributeVo);
        };
        respVo.setAttributes(attributeVos);
        return respVo;
    }
}
